package com.task.controller;

import com.basemodel.Result;
import com.common.annotation.RepeatSubmit;
import com.exception.ApiException;
import com.task.model.SysJobPO;
import com.task.service.SysJobService;
import com.util.MyUUID;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author: cxt
 * @time: 2021/11/16
 * 1、执行任务
 * 2、并发处理测试
 */
@RestController
@RequestMapping("/sys/job")
@Slf4j
@Api(tags = "并发")
public class SysJobController {

    private final SysJobService sysJobService;
    private final StringRedisTemplate redisTemplate;
    private final RedissonClient redissonClient;

    @GetMapping("/get")
    @ApiOperation("获取多线程")
    @RepeatSubmit()
    public String getSysJob(@RequestParam String job) {
        System.out.println("接收到的参数：" + job);
        List<SysJobPO> jobList = sysJobService.getSysJobAll(SysJobPO.STATE_NORMAL);
        return "成功！";
    }

    @ApiOperation("设置库存")
    @GetMapping("/setStock")
    public Result<String> setStock(@RequestParam Integer stock) {
        String re = "";
        if (stock != null && stock > 0) {
            redisTemplate.opsForValue().set("stock", String.valueOf(stock));
            re = "设置成功";
        } else {
            throw new ApiException("库存数据异常");
        }
        return Result.success(re);
    }

    @ApiOperation("测试高并发")
    @GetMapping("/own")
    public String highConcurrency() {
        String lockKey = "testLock";
        // 简单的分布式锁实现
        // 给每个线程分配一个id，用于判断是否释放自己的锁
        String currentLock = MyUUID.createOrderNo(123L, 3);
        ValueOperations<String, String> ops = redisTemplate.opsForValue();
        // 存在问题是：这个锁的超时时间不能确定，比如有时候下面执行特别慢，还没有执行完成，锁超时失效，后面来的再次加锁，出现问题
        // 出现上述问题的本质是其他线程因为超时释放掉了自己刚加的锁，解决办法就是自己的锁只能自己释放
        // 这里的超时时间问题，可以使用在加锁成功之后开启一个线程，进行锁的续命，只要当前线程没有执行完成，锁就要一直存在，已有很多较为完善的框架如redisson
        Boolean ifAbsent = ops.setIfAbsent(lockKey, currentLock, 10, TimeUnit.SECONDS);
        if (Boolean.FALSE.equals(ifAbsent)) {
            throw new ApiException("访问人员过多，稍后重试");
        }
        String result = "";
        try {
            // 从redis获取库存
            String stock = redisTemplate.opsForValue().get("stock");
            int integer = 0;
            if (stock != null && (integer = Integer.parseInt(stock)) > 0) {
                int remaining = integer - 1;
                // 减库存
                redisTemplate.opsForValue().set("stock", String.valueOf(remaining));
                log.info("抢购成功：{}", stock);
                result = "抢购成功";
            } else {
                result = "库存不足";
            }
        } finally {
            // 释放锁，且只能自己释放自己的锁，假如自己的锁超时失效之后就跳过
            // 这里也有小概率出现问题，所以保证下面的两行代码是原子性执行
            if (currentLock.equals(redisTemplate.opsForValue().get(lockKey))) {
                redisTemplate.delete(lockKey);
            }
        }
        return result;
    }

    @ApiOperation("上述测试的redisson版本解决")
    @GetMapping("/redissonH")
    public String highConcurrency1(@RequestParam String uId) {
        String lockKey = "testLock";
        RLock lock = redissonClient.getLock(lockKey + uId);
        String result = "";
        try {
            lock.lock(); // 加锁
            // 从redis获取库存
            String stock = redisTemplate.opsForValue().get("stock");
            int integer = 0;
            if (stock != null && (integer = Integer.parseInt(stock)) > 0) {
                int remaining = integer - 1;
                // 减库存
                redisTemplate.opsForValue().set("stock", String.valueOf(remaining));
                // 异步生成订单
                Thread.sleep(100);
                log.info("抢购成功：{}", stock);
                result = "抢购成功";
            } else {
                result = "库存不足";
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 释放锁
            lock.unlock();
        }
        return result;
    }

    @Autowired
    public SysJobController(SysJobService sysJobService, StringRedisTemplate redisTemplate, RedissonClient redissonClient) {
        this.sysJobService = sysJobService;
        this.redisTemplate = redisTemplate;
        this.redissonClient = redissonClient;
    }
}
